package com.its.common.net.server.impl;

import com.its.common.net.core.codec.DecoderBuildParams;
import com.its.common.net.core.handler.TailHandler;
import com.its.common.net.server.BaseServer;
import com.its.common.net.server.channel.ChannelHelper;
import com.its.common.net.server.channel.DefaultNetChannelListener;
import com.its.common.net.server.handler.ConnectManageHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;
import java.util.List;

/**
 * @Author xiaxp
 * @Date 9/1/22 22:47
 * @Description
 */
@Slf4j
public class TcpServer extends BaseServer<Channel> {

    public TcpServer(int port, DecoderBuildParams decoderParams, List<SimpleChannelInboundHandler> handlers) {
        super(port, handlers);
        this.decoderParams = decoderParams;
    }

    @Override
    public ChannelFuture doStart(int port) {
        boss = new NioEventLoopGroup();
        worker = new NioEventLoopGroup(4);
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(boss, worker)
            .handler(new LoggingHandler(LogLevel.INFO))
            .channel(NioServerSocketChannel.class)
            .option(ChannelOption.SO_BACKLOG, 1024)
            .option(ChannelOption.SO_REUSEADDR, true)
            .childOption(ChannelOption.SO_KEEPALIVE, false)
            .childOption(ChannelOption.TCP_NODELAY, true)
            .childOption(ChannelOption.SO_SNDBUF, READ_WRITE_BUF_CAP)
            .childOption(ChannelOption.SO_RCVBUF, READ_WRITE_BUF_CAP)
            .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
            .childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel channel) throws Exception {
                    ChannelPipeline pipeline = channel.pipeline();
                    ByteToMessageDecoder decoder = decoderParams.buildDecoderInstance();
                    pipeline.addLast(defaultEventExecutorGroup, decoder);
                    pipeline.addLast(defaultEventExecutorGroup,
                        new ConnectManageHandler(DefaultNetChannelListener.getInstance()));
                    handlers.forEach((handler) -> pipeline.addLast(defaultEventExecutorGroup, handler));
                    pipeline.addLast(defaultEventExecutorGroup, new TailHandler());
                }
            });

        ChannelFuture future = bootstrap.bind(port);
        return future;
    }

    @Override
    public void sendToAll(ByteBuf content) {
        List<Channel> channels = ChannelHelper.getAllChannels();
        channels.stream().forEach(channel -> send(channel, Unpooled.copiedBuffer(content)));
        if (content.refCnt() > 0) {
            ReferenceCountUtil.safeRelease(content);
        }
    }

    @Override
    public void doAfterStartUp(Channel channel) {
    }
}
